7939d9bf4b3fa60c47ed864fc4fd54ec22363e66
[jalview.git] / src / jalview / appletgui / AppletJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.appletgui;
20
21 import java.util.*;
22 import java.awt.*;
23 import java.awt.event.*;
24
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 jalview.schemes.*;
34
35 public class AppletJmol extends EmbmenuFrame implements StructureListener,
36         JmolStatusListener, KeyListener, ActionListener, ItemListener
37
38 {
39   Menu fileMenu = new Menu("File");
40
41   Menu viewMenu = new Menu("View");
42
43   Menu coloursMenu = new Menu("Colours");
44
45   Menu chainMenu = new Menu("Show Chain");
46
47   Menu helpMenu = new Menu("Help");
48
49   MenuItem mappingMenuItem = new MenuItem("View Mapping");
50
51   CheckboxMenuItem seqColour = new CheckboxMenuItem("By Sequence", true);
52
53   MenuItem chain = new MenuItem("By Chain");
54
55   MenuItem charge = new MenuItem("Charge & Cysteine");
56
57   MenuItem zappo = new MenuItem("Zappo");
58
59   MenuItem taylor = new MenuItem("Taylor");
60
61   MenuItem hydro = new MenuItem("Hydrophobicity");
62
63   MenuItem helix = new MenuItem("Helix Propensity");
64
65   MenuItem strand = new MenuItem("Strand Propensity");
66
67   MenuItem turn = new MenuItem("Turn Propensity");
68
69   MenuItem buried = new MenuItem("Buried Index");
70
71   MenuItem user = new MenuItem("User Defined Colours");
72
73   MenuItem jmolHelp = new MenuItem("Jmol Help");
74
75   JmolViewer viewer;
76
77   JmolPopup jmolpopup;
78
79   Panel scriptWindow;
80
81   TextField inputLine;
82
83   TextArea history;
84
85   SequenceI[] sequence;
86
87   String[] chains;
88
89   StructureSelectionManager ssm;
90
91   RenderPanel renderPanel;
92
93   AlignmentPanel ap;
94
95   String fileLoadingError;
96
97   boolean loadedInline;
98
99   PDBEntry pdbentry;
100
101   boolean colourBySequence = true;
102
103   Vector atomsPicked = new Vector();
104
105   /**
106    * datasource protocol for access to PDBEntry
107    */
108   String protocol = null;
109
110   public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
111           AlignmentPanel ap, String protocol)
112   {
113     this.ap = ap;
114     this.sequence = seq;
115     this.chains = chains;
116     this.pdbentry = pdbentry;
117     this.protocol = protocol;
118     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
119     {
120       if (jalview.bin.JalviewLite.debug)
121       {
122         System.err.println("Setting PDB id for file " + pdbentry.getFile());
123         if (protocol.equals(AppletFormatAdapter.PASTE))
124         {
125           pdbentry.setId("PASTED PDB"
126                   + (chains == null ? "_" : chains.toString()));
127         }
128         else
129         {
130           pdbentry.setId(pdbentry.getFile());
131         }
132       }
133     }
134     if (jalview.bin.JalviewLite.debug)
135     {
136       System.err
137               .println("AppletJmol: PDB ID is '" + pdbentry.getId() + "'");
138     }
139
140     String alreadyMapped = StructureSelectionManager
141             .getStructureSelectionManager().alreadyMappedToFile(
142                     pdbentry.getId());
143     MCview.PDBfile reader = null;
144     if (alreadyMapped != null)
145     {
146       reader = StructureSelectionManager.getStructureSelectionManager()
147               .setMapping(seq, chains, pdbentry.getFile(), protocol);
148       // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
149       // FOR NOW, LETS JUST OPEN A NEW WINDOW
150     }
151     MenuBar menuBar = new MenuBar();
152     menuBar.add(fileMenu);
153     fileMenu.add(mappingMenuItem);
154     menuBar.add(viewMenu);
155     mappingMenuItem.addActionListener(this);
156     viewMenu.add(chainMenu);
157     menuBar.add(coloursMenu);
158     menuBar.add(helpMenu);
159
160     charge.addActionListener(this);
161     hydro.addActionListener(this);
162     chain.addActionListener(this);
163     seqColour.addItemListener(this);
164     zappo.addActionListener(this);
165     taylor.addActionListener(this);
166     helix.addActionListener(this);
167     strand.addActionListener(this);
168     turn.addActionListener(this);
169     buried.addActionListener(this);
170     user.addActionListener(this);
171
172     jmolHelp.addActionListener(this);
173
174     coloursMenu.add(seqColour);
175     coloursMenu.add(chain);
176     coloursMenu.add(charge);
177     coloursMenu.add(zappo);
178     coloursMenu.add(taylor);
179     coloursMenu.add(hydro);
180     coloursMenu.add(helix);
181     coloursMenu.add(strand);
182     coloursMenu.add(turn);
183     coloursMenu.add(buried);
184     coloursMenu.add(user);
185
186     helpMenu.add(jmolHelp);
187
188     setMenuBar(menuBar);
189
190     renderPanel = new RenderPanel();
191     embedMenuIfNeeded(renderPanel);
192     this.add(renderPanel, BorderLayout.CENTER);
193     viewer = JmolViewer.allocateViewer(renderPanel,
194             new SmarterJmolAdapter());
195
196     viewer.setAppletContext("jalview", ap.av.applet.getDocumentBase(),
197             ap.av.applet.getCodeBase(), null);
198
199     viewer.setJmolStatusListener(this);
200
201     jmolpopup = JmolPopup.newJmolPopup(viewer);
202
203     this.addWindowListener(new WindowAdapter()
204     {
205       public void windowClosing(WindowEvent evt)
206       {
207         closeViewer();
208       }
209     });
210
211     if (pdbentry.getFile() != null)
212     {
213       // import structure data from pdbentry.getFile based on given protocol
214       if (protocol.equals(AppletFormatAdapter.PASTE))
215       {
216         loadInline(pdbentry.getFile());
217       }
218       else if (protocol.equals(AppletFormatAdapter.FILE)
219               || protocol.equals(AppletFormatAdapter.URL))
220       {
221         viewer.openFile(pdbentry.getFile());
222       }
223       else
224       {
225         // probably CLASSLOADER based datasource..
226         // Try and get a reader on the datasource, and pass that to Jmol
227         try
228         {
229           java.io.Reader freader = null;
230           if (reader != null)
231           {
232             if (jalview.bin.JalviewLite.debug)
233             {
234               System.err
235                       .println("AppletJmol:Trying to reuse existing PDBfile IO parser.");
236             }
237             // re-use the one we opened earlier
238             freader = reader.getReader();
239           }
240           if (freader == null)
241           {
242             if (jalview.bin.JalviewLite.debug)
243             {
244               System.err
245                       .println("AppletJmol:Creating new PDBfile IO parser.");
246             }
247             FileParse fp = new FileParse(pdbentry.getFile(), protocol);
248             fp.mark();
249             // reader = new MCview.PDBfile(fp);
250             // could set ID, etc.
251             // if (!reader.isValid())
252             // {
253             // throw new Exception("Invalid datasource.
254             // "+reader.getWarningMessage());
255             // }
256             // fp.reset();
257             freader = fp.getReader();
258           }
259           if (freader == null)
260           {
261             throw new Exception(
262                     "Invalid datasource. Could not obtain Reader.");
263           }
264           viewer.openReader(pdbentry.getFile(), pdbentry.getId(), freader);
265         } catch (Exception e)
266         {
267           // give up!
268           System.err.println("Couldn't access pdbentry id="
269                   + pdbentry.getId() + " and file=" + pdbentry.getFile()
270                   + " using protocol=" + protocol);
271           e.printStackTrace();
272         }
273       }
274     }
275
276     jalview.bin.JalviewLite.addFrame(this, "Jmol", 400, 400);
277   }
278
279   public void loadInline(String string)
280   {
281     loadedInline = true;
282     viewer.openStringInline(string);
283   }
284
285   void setChainMenuItems(Vector chains)
286   {
287     chainMenu.removeAll();
288
289     MenuItem menuItem = new MenuItem("All");
290     menuItem.addActionListener(this);
291
292     chainMenu.add(menuItem);
293
294     CheckboxMenuItem menuItemCB;
295     for (int c = 0; c < chains.size(); c++)
296     {
297       menuItemCB = new CheckboxMenuItem(chains.elementAt(c).toString(),
298               true);
299       menuItemCB.addItemListener(this);
300       chainMenu.add(menuItemCB);
301     }
302   }
303
304   boolean allChainsSelected = false;
305
306   void centerViewer()
307   {
308     StringBuffer cmd = new StringBuffer();
309     for (int i = 0; i < chainMenu.getItemCount(); i++)
310     {
311       if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
312       {
313         CheckboxMenuItem item = (CheckboxMenuItem) chainMenu.getItem(i);
314         if (item.getState())
315           cmd.append(":" + item.getLabel() + " or ");
316       }
317     }
318
319     if (cmd.length() > 0)
320       cmd.setLength(cmd.length() - 4);
321
322     viewer
323             .evalString("select *;restrict " + cmd + ";cartoon;center "
324                     + cmd);
325   }
326
327   void closeViewer()
328   {
329     viewer.setModeMouse(org.jmol.viewer.JmolConstants.MOUSE_NONE);
330     viewer.evalStringQuiet("zap");
331     viewer.setJmolStatusListener(null);
332     viewer = null;
333
334     // We'll need to find out what other
335     // listeners need to be shut down in Jmol
336     StructureSelectionManager.getStructureSelectionManager()
337             .removeStructureViewerListener(this, pdbentry.getId());
338
339     this.setVisible(false);
340   }
341
342   public void actionPerformed(ActionEvent evt)
343   {
344     if (evt.getSource() == mappingMenuItem)
345     {
346       jalview.appletgui.CutAndPasteTransfer cap = new jalview.appletgui.CutAndPasteTransfer(
347               false, null);
348       Frame frame = new Frame();
349       frame.add(cap);
350
351       jalview.bin.JalviewLite.addFrame(frame, "PDB - Sequence Mapping",
352               550, 600);
353       cap.setText(StructureSelectionManager.getStructureSelectionManager()
354               .printMapping(pdbentry.getFile()));
355     }
356     else if (evt.getSource() == charge)
357     {
358       colourBySequence = false;
359       seqColour.setState(false);
360       viewer
361               .evalStringQuiet("select *;color white;select ASP,GLU;color red;"
362                       + "select LYS,ARG;color blue;select CYS;color yellow");
363     }
364
365     else if (evt.getSource() == chain)
366     {
367       colourBySequence = false;
368       seqColour.setState(false);
369       viewer.evalStringQuiet("select *;color chain");
370     }
371     else if (evt.getSource() == zappo)
372     {
373       setJalviewColourScheme(new ZappoColourScheme());
374     }
375     else if (evt.getSource() == taylor)
376     {
377       setJalviewColourScheme(new TaylorColourScheme());
378     }
379     else if (evt.getSource() == hydro)
380     {
381       setJalviewColourScheme(new HydrophobicColourScheme());
382     }
383     else if (evt.getSource() == helix)
384     {
385       setJalviewColourScheme(new HelixColourScheme());
386     }
387     else if (evt.getSource() == strand)
388     {
389       setJalviewColourScheme(new StrandColourScheme());
390     }
391     else if (evt.getSource() == turn)
392     {
393       setJalviewColourScheme(new TurnColourScheme());
394     }
395     else if (evt.getSource() == buried)
396     {
397       setJalviewColourScheme(new BuriedColourScheme());
398     }
399     else if (evt.getSource() == user)
400     {
401       new UserDefinedColours(this);
402     }
403     else if (evt.getSource() == jmolHelp)
404     {
405       try
406       {
407         ap.av.applet.getAppletContext().showDocument(
408                 new java.net.URL(
409                         "http://jmol.sourceforge.net/docs/JmolUserGuide/"),
410                 "jmolHelp");
411       } catch (java.net.MalformedURLException ex)
412       {
413       }
414     }
415     else
416     {
417       allChainsSelected = true;
418       for (int i = 0; i < chainMenu.getItemCount(); i++)
419       {
420         if (chainMenu.getItem(i) instanceof CheckboxMenuItem)
421           ((CheckboxMenuItem) chainMenu.getItem(i)).setState(true);
422       }
423       centerViewer();
424       allChainsSelected = false;
425     }
426   }
427
428   public void setJalviewColourScheme(ColourSchemeI cs)
429   {
430     colourBySequence = false;
431     seqColour.setState(false);
432
433     if (cs == null)
434       return;
435
436     String res;
437     int index;
438     Color col;
439
440     Enumeration en = ResidueProperties.aa3Hash.keys();
441     StringBuffer command = new StringBuffer("select *;color white;");
442     while (en.hasMoreElements())
443     {
444       res = en.nextElement().toString();
445       index = ((Integer) ResidueProperties.aa3Hash.get(res)).intValue();
446       if (index > 20)
447         continue;
448
449       col = cs.findColour(ResidueProperties.aa[index].charAt(0));
450
451       command.append("select " + res + ";color[" + col.getRed() + ","
452               + col.getGreen() + "," + col.getBlue() + "];");
453     }
454
455     viewer.evalStringQuiet(command.toString());
456   }
457
458   public void itemStateChanged(ItemEvent evt)
459   {
460     if (evt.getSource() == seqColour)
461     {
462       lastCommand = null;
463       colourBySequence = seqColour.getState();
464       colourBySequence(ap);
465     }
466     else if (!allChainsSelected)
467       centerViewer();
468   }
469
470   public void keyPressed(KeyEvent evt)
471   {
472     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
473     {
474       viewer.evalString(inputLine.getText());
475       history.append("\n$ " + inputLine.getText());
476       inputLine.setText("");
477     }
478
479   }
480
481   public void keyTyped(KeyEvent evt)
482   {
483   }
484
485   public void keyReleased(KeyEvent evt)
486   {
487   }
488
489   // ////////////////////////////////
490   // /StructureListener
491   public String getPdbFile()
492   {
493     return "???";
494   }
495
496   String lastMessage;
497
498   public void mouseOverStructure(int atomIndex, String strInfo)
499   {
500     int pdbResNum;
501
502     int chainSeparator = strInfo.indexOf(":");
503
504     if (chainSeparator == -1)
505       chainSeparator = strInfo.indexOf(".");
506
507     pdbResNum = Integer.parseInt(strInfo.substring(
508             strInfo.indexOf("]") + 1, chainSeparator));
509
510     String chainId;
511
512     if (strInfo.indexOf(":") > -1)
513       chainId = strInfo.substring(strInfo.indexOf(":") + 1, strInfo
514               .indexOf("."));
515     else
516     {
517       chainId = " ";
518     }
519
520     if (lastMessage == null || !lastMessage.equals(strInfo))
521       ssm.mouseOverStructure(pdbResNum, chainId, pdbentry.getFile());
522
523     lastMessage = strInfo;
524   }
525
526   StringBuffer resetLastRes = new StringBuffer();
527
528   StringBuffer eval = new StringBuffer();
529
530   public void highlightAtom(int atomIndex, int pdbResNum, String chain,
531           String pdbfile)
532   {
533     if (!pdbfile.equals(pdbentry.getFile()))
534       return;
535
536     if (resetLastRes.length() > 0)
537     {
538       viewer.evalStringQuiet(resetLastRes.toString());
539     }
540
541     eval.setLength(0);
542     eval.append("select " + pdbResNum);
543
544     resetLastRes.setLength(0);
545     resetLastRes.append("select " + pdbResNum);
546
547     if (!chain.equals(" "))
548     {
549       eval.append(":" + chain);
550       resetLastRes.append(":" + chain);
551     }
552
553     eval.append(";wireframe 100;" + eval.toString() + ".CA;");
554
555     resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
556             + ".CA;spacefill 0;");
557
558     eval.append("spacefill 200;select none");
559
560     viewer.evalStringQuiet(eval.toString());
561
562   }
563
564   public void updateColours(Object source)
565   {
566     colourBySequence((AlignmentPanel) source);
567   }
568
569   // End StructureListener
570   // //////////////////////////
571
572   public Color getColour(int atomIndex, int pdbResNum, String chain,
573           String pdbfile)
574   {
575     if (!pdbfile.equals(pdbentry.getFile()))
576       return null;
577
578     return new Color(viewer.getAtomArgb(atomIndex));
579   }
580
581   String lastCommand;
582
583   FeatureRenderer fr = null;
584
585   public void colourBySequence(AlignmentPanel sourceap)
586   {
587     this.ap = sourceap;
588
589     if (!colourBySequence)
590       return;
591
592     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
593
594     if (mapping.length < 1)
595       return;
596
597     SequenceRenderer sr = new SequenceRenderer(ap.av);
598
599     boolean showFeatures = false;
600
601     if (ap.av.showSequenceFeatures)
602     {
603       showFeatures = true;
604       if (fr == null)
605       {
606         fr = new jalview.appletgui.FeatureRenderer(ap.av);
607       }
608
609       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
610     }
611
612     StringBuffer command = new StringBuffer();
613
614     int lastPos = -1;
615     for (int s = 0; s < sequence.length; s++)
616     {
617       for (int sp, m = 0; m < mapping.length; m++)
618       {
619         if (mapping[m].getSequence() == sequence[s]
620                 && (sp = ap.av.alignment.findIndex(sequence[s])) > -1)
621         {
622           SequenceI asp = ap.av.alignment.getSequenceAt(sp);
623           for (int r = 0; r < asp.getLength(); r++)
624           {
625             // no mapping to gaps in sequence
626             if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
627             {
628               continue;
629             }
630             int pos = mapping[m].getPDBResNum(asp.findPosition(r));
631
632             if (pos < 1 || pos == lastPos)
633               continue;
634
635             lastPos = pos;
636
637             Color col = sr.getResidueBoxColour(sequence[s], r);
638
639             if (showFeatures)
640               col = fr.findFeatureColour(col, sequence[s], r);
641
642             if (command.toString().endsWith(
643                     ":" + mapping[m].getChain() + ";color[" + col.getRed()
644                             + "," + col.getGreen() + "," + col.getBlue()
645                             + "]"))
646             {
647               command = condenseCommand(command.toString(), pos);
648               continue;
649             }
650
651             command.append(";select " + pos);
652
653             if (!mapping[m].getChain().equals(" "))
654             {
655               command.append(":" + mapping[m].getChain());
656             }
657
658             command.append(";color[" + col.getRed() + "," + col.getGreen()
659                     + "," + col.getBlue() + "]");
660           }
661           break;
662         }
663       }
664     }
665
666     if (lastCommand == null || !lastCommand.equals(command.toString()))
667     {
668       viewer.evalStringQuiet(command.toString());
669     }
670     lastCommand = command.toString();
671   }
672
673   StringBuffer condenseCommand(String command, int pos)
674   {
675
676     StringBuffer sb = new StringBuffer(command.substring(0, command
677             .lastIndexOf("select") + 7));
678
679     command = command.substring(sb.length());
680
681     String start;
682
683     if (command.indexOf("-") > -1)
684     {
685       start = command.substring(0, command.indexOf("-"));
686     }
687     else
688     {
689       start = command.substring(0, command.indexOf(":"));
690     }
691
692     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
693
694     return sb;
695   }
696
697   // ///////////////////////////////
698   // JmolStatusListener
699
700   public String eval(String strEval)
701   {
702     // System.out.println(strEval);
703     // "# 'eval' is implemented only for the applet.";
704     return null;
705   }
706
707   public void createImage(String file, String type, int quality)
708   {
709   }
710
711   public void setCallbackFunction(String callbackType,
712           String callbackFunction)
713   {
714   }
715
716   public void notifyFileLoaded(String fullPathName, String fileName,
717           String modelName, Object clientFile, String errorMsg)
718   {
719     if (errorMsg != null)
720     {
721       fileLoadingError = errorMsg;
722       repaint();
723       return;
724     }
725
726     fileLoadingError = null;
727
728     if (fileName != null)
729     {
730       // FILE LOADED OK
731       jmolpopup.updateComputedMenus();
732       viewer
733               .evalStringQuiet("select backbone;restrict;cartoon;wireframe off;spacefill off");
734
735       ssm = StructureSelectionManager.getStructureSelectionManager();
736       MCview.PDBfile pdb;
737       if (loadedInline)
738       {
739         pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
740                 AppletFormatAdapter.PASTE);
741         pdbentry.setFile("INLINE" + pdb.id);
742       }
743       else
744       {
745         // TODO: Jmol can in principle retrieve from CLASSLOADER but this needs
746         // to be tested. See mantis bug
747         // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
748
749         pdb = ssm.setMapping(sequence, chains, pdbentry.getFile(),
750                 AppletFormatAdapter.URL);
751
752       }
753
754       pdbentry.setId(pdb.id);
755
756       ssm.addStructureViewerListener(this);
757
758       Vector chains = new Vector();
759       for (int i = 0; i < pdb.chains.size(); i++)
760       {
761         chains.addElement(((MCview.PDBChain) pdb.chains.elementAt(i)).id);
762       }
763       setChainMenuItems(chains);
764
765       colourBySequence(ap);
766
767       StringBuffer title = new StringBuffer(sequence[0].getName() + ":"
768               + pdbentry.getId());
769
770       if (pdbentry.getProperty() != null)
771       {
772         if (pdbentry.getProperty().get("method") != null)
773         {
774           title.append(" Method: ");
775           title.append(pdbentry.getProperty().get("method"));
776         }
777         if (pdbentry.getProperty().get("chains") != null)
778         {
779           title.append(" Chain:");
780           title.append(pdbentry.getProperty().get("chains"));
781         }
782       }
783
784       this.setTitle(title.toString());
785
786     }
787     else
788       return;
789   }
790
791   public void notifyFrameChanged(int frameNo)
792   {
793     boolean isAnimationRunning = (frameNo <= -2);
794   }
795
796   public void notifyScriptStart(String statusMessage, String additionalInfo)
797   {
798   }
799
800   public void sendConsoleEcho(String strEcho)
801   {
802     if (scriptWindow == null)
803       showConsole(true);
804
805     history.append("\n" + strEcho);
806   }
807
808   public void sendConsoleMessage(String strStatus)
809   {
810     if (history != null && strStatus != null
811             && !strStatus.equals("Script completed"))
812     {
813       history.append("\n" + strStatus);
814     }
815   }
816
817   public void notifyScriptTermination(String strStatus, int msWalltime)
818   {
819   }
820
821   public void handlePopupMenu(int x, int y)
822   {
823     jmolpopup.show(x, y);
824   }
825
826   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
827   {
828     notifyAtomPicked(iatom, strMeasure);
829   }
830
831   public void notifyNewDefaultModeMeasurement(int count, String strInfo)
832   {
833   }
834
835   public void notifyAtomPicked(int atomIndex, String strInfo)
836   {
837
838     int chainSeparator = strInfo.indexOf(":");
839
840     if (chainSeparator == -1)
841       chainSeparator = strInfo.indexOf(".");
842
843     String picked = strInfo.substring(strInfo.indexOf("]") + 1,
844             chainSeparator);
845
846     if (strInfo.indexOf(":") > -1)
847       picked += strInfo.substring(strInfo.indexOf(":") + 1, strInfo
848               .indexOf("."));
849
850     picked += ".CA";
851
852     if (!atomsPicked.contains(picked))
853     {
854       viewer.evalString("select " + picked + ";label %n %r:%c");
855       atomsPicked.addElement(picked);
856     }
857     else
858     {
859       viewer.evalString("select " + picked + ";label off");
860       atomsPicked.removeElement(picked);
861     }
862   }
863
864   public void notifyAtomHovered(int atomIndex, String strInfo)
865   {
866     mouseOverStructure(atomIndex, strInfo);
867   }
868
869   public void sendSyncScript(String script, String appletName)
870   {
871   }
872
873   public void showUrl(String url)
874   {
875     try
876     {
877       ap.av.applet.getAppletContext().showDocument(new java.net.URL(url),
878               "jmolOutput");
879     } catch (java.net.MalformedURLException ex)
880     {
881     }
882   }
883
884   public void showConsole(boolean showConsole)
885   {
886     if (scriptWindow == null)
887     {
888       scriptWindow = new Panel(new BorderLayout());
889       inputLine = new TextField();
890       history = new TextArea(5, 40);
891       scriptWindow.add(history, BorderLayout.CENTER);
892       scriptWindow.add(inputLine, BorderLayout.SOUTH);
893       add(scriptWindow, BorderLayout.SOUTH);
894       scriptWindow.setVisible(false);
895       history.setEditable(false);
896       inputLine.addKeyListener(this);
897     }
898
899     scriptWindow.setVisible(!scriptWindow.isVisible());
900     validate();
901   }
902
903   public float functionXY(String functionName, int x, int y)
904   {
905     return 0;
906   }
907
908   // /End JmolStatusListener
909   // /////////////////////////////
910
911   class RenderPanel extends Panel
912   {
913     Dimension currentSize = new Dimension();
914
915     Rectangle rectClip = new Rectangle();
916
917     public void update(Graphics g)
918     {
919       paint(g);
920     }
921
922     public void paint(Graphics g)
923     {
924       currentSize = this.getSize();
925       rectClip = g.getClipBounds();
926
927       if (viewer == null)
928       {
929         g.setColor(Color.black);
930         g.fillRect(0, 0, currentSize.width, currentSize.height);
931         g.setColor(Color.white);
932         g.setFont(new Font("Verdana", Font.BOLD, 14));
933         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
934       }
935       else
936       {
937         viewer.renderScreenImage(g, currentSize, rectClip);
938       }
939     }
940   }
941
942 }